#!/usr/bin/env python3
# _*_coding:utf-8 _*_

__author__ = "Gao"
from django.contrib.auth import hashers
from rest_framework_jwt.serializers import JSONWebTokenSerializer,RefreshJSONWebTokenSerializer
from rest_framework import serializers
from django.conf import settings
import importlib
from django.utils.translation import ugettext as _
from rest_framework_jwt.settings import api_settings
from datetime import datetime,timedelta
from calendar import timegm
from celery.exceptions import TimeoutError
from my_rest.exceptions import ServiceUnavilable,InnerError
import json
import jwt
from django.utils import timezone
import time


from core.tasks import user_tasks

jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
hasher = getattr(hashers,settings.AUTH_HASHER)    # 获取Hash算法
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


def jwt_payload_handler(user):
    """定制payload"""
    payload = {
        'id': user['id'],
        'code': user['code'],
        'username': user['username'],    # 之后直接从数据库取
        'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA,
        'is_admin': user['is_admin']
    }


    # Include original issued at time for a brand new token,
    # to allow token refresh
    if api_settings.JWT_ALLOW_REFRESH:    # 是否允许刷新JWT
        payload['orig_iat'] = timegm(
            datetime.utcnow().utctimetuple()
        )

    if api_settings.JWT_AUDIENCE is not None:
        payload['aud'] = api_settings.JWT_AUDIENCE

    if api_settings.JWT_ISSUER is not None:
        payload['iss'] = api_settings.JWT_ISSUER
    return payload


def authenticate(**credentials):
    """自己验证用户名和密码"""
    code = credentials.get('code')    # 工号
    password = credentials.get('password')    # 密码

    conditions = {'code':code}
    fields = ['id','code','username','password','is_admin','is_active']
    task_res = json.loads(user_tasks.get_users(*fields,**conditions))

    if task_res['status'] == 400:
        msg = _('参数错误')
        raise serializers.ValidationError(msg)
    try:
        user = task_res['data'][0]
    except IndexError:
        return None
    if user:
        user_password = user['password']
        salt = user_password.split('$')[2]
        if hasher.encode(hasher, password, salt) == user_password:
            if user['is_active']:
                return user
            else:
                msg = _('该账号已被禁用')
                raise serializers.ValidationError(msg)


class PasswordField(serializers.CharField):

    def __init__(self, *args, **kwargs):
        if 'style' not in kwargs:
            kwargs['style'] = {'input_type': 'password'}
        else:
            kwargs['style']['input_type'] = 'password'
        super(PasswordField, self).__init__(*args, **kwargs)


class MyJSONWebTokenSerializer(JSONWebTokenSerializer):
    def __init__(self, *args, **kwargs):
        """传入code和密码，获取Token"""
        super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)

        self.fields['code'] = serializers.CharField()    # 定义传入用户名字段
        self.fields['password'] = PasswordField(write_only=True)    # 定义传入密码字段


    # verification_key = serializers.CharField()    # 验证码的Key
    # verification_code = serializers.CharField()    # 验证码的Value

    def validate(self, attrs):
        # self.extra_validate(attrs)    # 进行额外验证

        credentials = {
            'code': attrs.get('code'),
            'password': attrs.get('password')
        }
        if all(credentials.values()):
            user = authenticate(**credentials)

            if user:
                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
        msg = _('用户名或密码错误')
        msg = msg.format(username_field=self.username_field)
        raise serializers.ValidationError(msg)

    # def extra_validate(self,attrs):
    #     """验证验证码"""
    #     verification_key = attrs.get('verification_key')
    #     verification_code = attrs.get('verification_code')
    #     if verification_key != verification_code:
    #         msg = '验证码错误或已过期'
    #         raise serializers.ValidationError(msg)


class MyRefreshJSONWebTokenSerializer(RefreshJSONWebTokenSerializer):
    """刷新Token时，检查账号状态"""
    def _check_user(self, payload):
        uid = payload.get('id')
        code = payload.get('code')
        username = payload.get('username')
        is_admin = payload.get('is_admin')


        if not all([uid,code,username ]):
            msg = _('不合法的token.')
            raise serializers.ValidationError(msg)

        # Make sure user exists
        # conditions = {'uid': uid}
        # t = user_tasks.get_users.delay(**conditions)
        # try:
        #     task_res = json.loads(t.get(timeout=10))
        # except TimeoutError:
        #     raise ServiceUnavilable()
        #
        # try:
        #     user = task_res['data'][0]
        # except IndexError:
        #     msg = _("该帐号不存在")
        #     raise serializers.ValidationError(msg)

        conditions = {'id': uid}
        fields = ['id', 'code', 'username', 'password', 'is_admin', 'is_active']
        task_res = json.loads(user_tasks.get_users(*fields, **conditions))

        try:
            user = task_res['data'][0]
        except IndexError:
            msg = _("该帐号不存在")
            raise serializers.ValidationError(msg)

        if not user['is_active']:
            msg = _('该账号已被禁用')
            raise serializers.ValidationError(msg)

        return user


    def _check_payload(self, token):
        # Check payload valid (based off of JSONWebTokenAuthentication,
        # may want to refactor)
        try:
            payload = jwt_decode_handler(token)
        except jwt.ExpiredSignature:
            msg = _('token已过期')
            raise serializers.ValidationError(msg)

        except jwt.DecodeError:
            msg = _('token不合法.')
            raise serializers.ValidationError(msg)

        return payload

    def validate(self, attrs):
        token = attrs['token']

        payload = self._check_payload(token=token)
        user = self._check_user(payload=payload)
        # Get and check 'orig_iat'
        orig_iat = payload.get('orig_iat')

        if orig_iat:    # 计算token最迟可以更新到哪天，之后必须重新获取token
            # Verify expiration
            refresh_limit = api_settings.JWT_REFRESH_EXPIRATION_DELTA

            if isinstance(refresh_limit, timedelta):
                refresh_limit = (refresh_limit.days * 24 * 3600 +
                                 refresh_limit.seconds)

            expiration_timestamp = orig_iat + int(refresh_limit)
            now_timestamp = timegm(datetime.utcnow().utctimetuple())

            if now_timestamp > expiration_timestamp:
                msg = _('该Token无法继续更新')
                raise serializers.ValidationError(msg)
        else:
            msg = _('orig_iat field is required.')
            raise serializers.ValidationError(msg)
        """
        额外操作:
        1. 判断token是否在黑名单中
        2. 把旧的token加入黑名单
        
        """

        new_payload = jwt_payload_handler(user)
        new_payload['orig_iat'] = orig_iat

        return {
            'token': jwt_encode_handler(new_payload),
            'user': user
        }


class ChangePasswordSerializer(serializers.Serializer):
    """修改用户密码"""
    old_password = serializers.CharField(write_only=True)    # 原密码
    password = serializers.CharField()    # 新密码

    def validate(self, attrs):    # 校验原密码是否正确
        instance_password = self.instance.get('password')    # 数据库中保存的原密码
        salt = instance_password.split('$')[2]
        put_old_password = attrs.get('old_password')    # 传入的原密码
        put_password = attrs.get('password')    # 传入的新密码
        if hasher.encode(hasher, put_old_password, salt) != instance_password:
            raise serializers.ValidationError('原密码不正确')
        if put_old_password == put_password:
            raise serializers.ValidationError('新密码不能和旧密码相同')
        return attrs


    def update(self,instance, validated_data):
        validated_data.pop('old_password')
        password = validated_data.pop('password')
        hash_password = hasher.encode(hasher, password, hasher.salt(hasher))    # 加密新密码
        validated_data['password'] = hash_password

        tmp_instance = {}    # 更新user_obj并存入数据库
        for key in instance:
            tmp_instance[key] = validated_data.get(key,instance[key])
        task_res = json.loads(user_tasks.save_user(instance=instance,**tmp_instance))
        if task_res['status'] == 200:
            new_instance = task_res['data']
            return new_instance
        else:
            raise InnerError(task_res['detail'])


